package com.zj.study.handle;

import com.zj.study.pojo.FinalResultVO;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @author 赵赳
 * @since: 2021/9/23 9:17
 */
@Log4j2
@ControllerAdvice
public class GlobalExceptionHandler {


  /**
   * 处理器列表
   */
  private static final List<ExceptionHandler> exceptionHandlers = new ArrayList<>(8);

  // 将异常处理器放入缓存中
  static {
    exceptionHandlers.add(new ValidationExceptionHandler());
    exceptionHandlers.add(new RespExceptionHandler());
    exceptionHandlers.add(new ServerWebInputExceptionHandler());
  }

  /**
   * 添加处理器
   *
   * @param handlers 异常处理器数组
   * @return this
   */
  public GlobalExceptionHandler addHandlers(ExceptionHandler... handlers) {
    exceptionHandlers.addAll(Arrays.asList(handlers));
    return this;
  }

  /**
   * 默认处理器
   */
  private static final ExceptionHandler DEFAULT_HANDLER = new DefaultExceptionHandler();

  /**
   * 异常处理器匹配，执行该处理器的handler方法
   *
   * @param throwable 异常类型
   * @return 异常处理结果
   */
  @org.springframework.web.bind.annotation.ExceptionHandler(RuntimeException.class)
  @ResponseBody
  public ResponseEntity<?> handleException(Throwable throwable) {
    return exceptionHandlers.stream()
        .filter(exceptionHandler -> exceptionHandler.match(throwable.getClass()))
        .findFirst()
        .orElse(DEFAULT_HANDLER)
        .handler(throwable);
  }

  /**
   * 当找不到能够处理的处理器使用默认处理器进行处理
   */
  private static class DefaultExceptionHandler implements ExceptionHandler {

    @Override
    public boolean match(Class<?> clazz) {
      return true;
    }

    @Override
    public ResponseEntity<?> handler(Throwable throwable) {
      log.error("默认异常处理，类型：{}, 信息：{}, 栈:{}",
          throwable.getClass().getSimpleName(),
          throwable.getMessage(),
          throwable.getStackTrace());
      return response(FinalResultVO.fail(DEFAULT_STATUS));
    }
  }
}